// 1.返回值（最常用）
function fn() {
    var name = 'hello'
    return function() {
        return name
    }
}
var fnc = fn()
console.log(fnc()); // hello
// 以闭包的形式将name返回。

// 2.函数赋值
var fn2

function fn() {
    var name = 'hello'
        // 将函数赋值给fn2
    fn2 = function() {
        return name
    }
}
// 要先执行进行赋值
fn()
    // 执行输出fn2
console.log(fn2());

// 3.函数参数
function fn() {
    var name = 'hello'
    return function callback() {
        return name
    }
}
var fn1 = fn() // 执行函数将返回值callback函数赋值给fn1

function fn2(f) {
    // 将函数作为参数传入
    console.log(f()); // 执行函数并输出
}
fn2(fn1) // 执行输出fn2
    // 用闭包返回一个函数，把此函数作为另一个函数的参数，在另一个函数里执行这个函数，最终输出hello

// 4. IIFE自执行函数
(function() {
    var name = 'hello'
    var fn1 = function() { // ② 赋值函数
        return name // ① 返回值（以闭包的形式将name返回）
    }
    fn2(fn1) // ③ 函数参数
})()

function fn2(f) {
    // 将函数作为参数传入
    console.log(f());
}

// 5. 循环赋值
// 每秒执行1次，分别输出1-10
for (var i = 1; i <= 10; i++) {
    (function(j) {
        // j来接收
        setTimeout(() => {
            console.log(j);
        }, j * 1000);
    })(i) // i作为实参传入
}
// 6.getter和setter
function fn() {
    var name = 'hello'
    setName = function(n) {
        name = n
    }
    getName = function() {
        return name
    }
    return {
        setName: setName,
        getName: getName
    }
}
var fn1 = fn() // 返回对象，属性setName和getName是两个函数
console.log(fn1.getName(), 1); // getter
fn1.setName('world') // setter 修改闭包里的name
console.log(fn1.getName(), 2); // getter

// 第一次输出hello，用setter以后再输出world,这样做可以封装成公共方法，防止不想暴露的属性和函数暴露在外面。

// 7.迭代器（执行一次函数往下取一个值）
var arr = ['aa', 'bb', 'cc']

function incre(arr) {
    var i = 0;
    return function() {
        return arr[i++] || '数组值已经遍历完'
    }
}
var next = incre(arr)
console.log(next()); // aa
console.log(next()); // bb
console.log(next()); // cc
console.log(next()); // 数组值已经遍历完

// 8.首次区分（相同的参数，函数不会重复执行）
var fn = (function() {
    var arr = [] // 用来缓存的数组
    return function(val) {
        if (arr.indexOf(val) == -1) {
            arr.push(val)
            console.log('函数被执行了', arr);
        } else {
            console.log('此次函数不需要被执行');
        }
        console.log('函数调用完打印一下，方便查看已缓存的数组', arr);
    }
})()
fn(10)
fn(10)
fn(1000)
fn(200)
fn(1000)
    /** 8打印结果：
            函数被执行了 [10]
            函数调用完打印一下，方便查看已缓存的数组 [10]
            此次函数不需要被执行
            函数调用完打印一下，方便查看已缓存的数组 [10]
            函数被执行了 (2) [10, 1000]
            函数调用完打印一下，方便查看已缓存的数组 (2) [10, 1000]
            函数被执行了 (3) [10, 1000, 200]
            函数调用完打印一下，方便查看已缓存的数组 (3) [10, 1000, 200]
            此次函数不需要被执行
    */
    //    可以明显看出首次执行的会被存起来，再次执行直接取。

// 9.缓存
// 比如求和操作，如果没有缓存，每次调用都要重新计算，采用缓存已经执行过的去查找，找到了就直接返回，不需要重新计算。
var fn = (function() {
    var cache = {}
    var calc = function(arr) {
        var sum = 0
        for (var i = 0; i < arr.length; i++) {
            sum += arr[i]
        }
        return sum
    }
    return function() {
        var args = Array.prototype.slice.call(arguments, 0) // arguments 转换成数组
        var key = args.join(',')
        var result, tSum = cache[key]
        if (tSum) { // 如果缓存有
            console.log('从缓存中取：', cache);
            result = tSum
        } else {
            // 重新计算，并存入缓存，同时赋给result
            result = cache[key] = calc(args)
            console.log('存入缓存：', cache);
        }
        return result
    }
})()
fn(1, 2, 3, 4, 5)
fn(1, 2, 3, 4, 5)
fn(1, 2, 3, 4, 5, 6)
fn(1, 2, 3, 4, 5, 8)
fn(1, 2, 3, 4, 5, 6)
    /**打印结果：
         *  存入缓存： {1,2,3,4,5: 15}
            从缓存中取： {1,2,3,4,5: 15}
            存入缓存： {1,2,3,4,5: 15, 1,2,3,4,5,6: 21}
            存入缓存： {1,2,3,4,5: 15, 1,2,3,4,5,6: 21, 1,2,3,4,5,8: 23}
            从缓存中取： {1,2,3,4,5: 15, 1,2,3,4,5,6: 21, 1,2,3,4,5,8: 23}
        */